package com.bencordingley.remotecontrol;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;

import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.WifiManager;

public class NetworkClient {

	private static final byte COMMAND_KEEP_ALIVE = 0;
	private static final byte COMMAND_EXIT = 1;
	private static final byte COMMAND_GET_SYSTEM_VOLUME = 2;
	private static final byte COMMAND_SET_SYSTEM_VOLUME = 3;
	private static final byte COMMAND_PLAY = 4;
	private static final byte COMMAND_STOP = 5;
	private static final byte COMMAND_PAUSE = 6;
	private static final byte COMMAND_NEXT = 7;
	private static final byte COMMAND_PREVIOUS = 8;
	private static final byte COMMAND_GET_VOLUME = 9;
	private static final byte COMMAND_SET_VOLUME = 10;
	private static final byte COMMAND_GET_FULLSCREEN = 11;
	private static final byte COMMAND_SET_FULLSCREEN = 12;
	private static final byte COMMAND_GET_DURATION = 13;
	private static final byte COMMAND_GET_POSITION = 14;
	private static final byte COMMAND_SET_POSITION = 15;
	private static final byte COMMAND_EXIT_PLAYER = 16;
	private static final byte COMMAND_LIST_DIRECTORY = 17;
	private static final byte COMMAND_SET_PLAYLIST = 18;
	private static final byte COMMAND_GET_RATE = 19;
	private static final byte COMMAND_SET_RATE = 20;
	private static final byte COMMAND_IS_RUNNING = 21;
	private static final byte COMMAND_IS_PAUSED = 22;
	private static final byte COMMAND_SKIP_FORWARD_SHORT = 23;
	private static final byte COMMAND_SKIP_FORWARD_LONG = 24;
	private static final byte COMMAND_SKIP_BACKWARD_SHORT = 25;
	private static final byte COMMAND_SKIP_BACKWARD_LONG = 26;
	
	private static final int DISCOVERY_PORT = 65432;

	private Socket client;
	private InputStream in;
	private volatile OutputStream out;

	private String hostname;

	private NetworkClient(InetAddress host, int port) throws IOException {
		client = new Socket(host, port);
		in = client.getInputStream();
		out = client.getOutputStream();
	}

	public static NetworkClient connect(Context context) {
		NetworkClient ret = null;
		try {
			InetAddress broadcastAddress = getBroadcastAddress(context);

			DatagramSocket socket = new DatagramSocket(DISCOVERY_PORT - 1);
			socket.setBroadcast(true);
			DatagramPacket packet = new DatagramPacket(new byte[] { COMMAND_KEEP_ALIVE }, 1, broadcastAddress, DISCOVERY_PORT);
			socket.send(packet);

			byte[] buf = new byte[1024];
			packet = new DatagramPacket(buf, buf.length);
			socket.receive(packet);
			InetAddress serverAddress = packet.getAddress();
			byte[] data = packet.getData();
			ByteArrayInputStream in = new ByteArrayInputStream(data);
			DataInputStream dataIn = new DataInputStream(in);
			int serverPort = dataIn.readInt();
			System.out.println(serverAddress + ":" + serverPort);
			ret = new NetworkClient(serverAddress, serverPort);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return ret;
	}

	private static InetAddress getBroadcastAddress(Context context) throws IOException {
		WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
		DhcpInfo dhcp = wifi.getDhcpInfo();
		// handle null somehow

		int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
		byte[] quads = new byte[4];
		for (int k = 0; k < 4; k++)
			quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
		return InetAddress.getByAddress(quads);
	}

	// public static NetworkClient connect(SharedPreferences prefs) {
	// NetworkClient ret = null;
	// String host = prefs.getString("hostname", "");
	// int port = Integer.parseInt(prefs.getString("port", "0"));
	// if (!host.equals("") && port > 0) {
	// try {
	// ret = new NetworkClient(host, port);
	// ret.setHostname(host);
	// } catch (IOException ex) {
	// ex.printStackTrace();
	// }
	// }
	// return ret;
	// }

	public void setHostname(String host) {
		hostname = host;
	}

	public String getHostname() {
		return hostname;
	}

	public byte getSystemVolume() throws IOException {
		byte vol = 0;
		out.write(COMMAND_GET_SYSTEM_VOLUME);
		vol = (byte) in.read();
		return vol;
	}

	public void setSystemVolume(byte volumePercent) throws IOException {
		byte[] data = { COMMAND_SET_SYSTEM_VOLUME, volumePercent };
		out.write(data);
	}

	public void close() throws IOException {
		out.close();
		in.close();
		client.close();
	}

	public void exitServer() throws IOException {
		out.write(COMMAND_EXIT);
	}

	public void play() throws IOException {
		out.write(COMMAND_PLAY);
	}

	public void pause() throws IOException {
		out.write(COMMAND_PAUSE);
	}

	public void stopPlaying() throws IOException {
		out.write(COMMAND_STOP);
	}

	public void next() throws IOException {
		out.write(COMMAND_NEXT);
	}

	public void previous() throws IOException {
		out.write(COMMAND_PREVIOUS);
	}

	public boolean isFullscreen() throws IOException {
		out.write(COMMAND_GET_FULLSCREEN);
		int fs = in.read();
		return fs != 0;
	}

	public void setFullscreen(boolean fullscreen) throws IOException {
		byte[] data = new byte[] { COMMAND_SET_FULLSCREEN, fullscreen ? (byte) 1 : (byte) 0 };
		out.write(data);
	}

	public byte getPlayerVolume() throws IOException {
		byte vol = 0;
		out.write(COMMAND_GET_VOLUME);
		vol = (byte) in.read();
		return vol;
	}

	public void setPlayerVolume(byte volumePercent) throws IOException {
		byte[] data = { COMMAND_SET_VOLUME, volumePercent };
		out.write(data);
	}

	public long getDuration() throws IOException {
		out.write(COMMAND_GET_DURATION);
		DataInputStream dataIn = new DataInputStream(in);
		return dataIn.readLong();
	}

	public long getPosition() throws IOException {
		out.write(COMMAND_GET_POSITION);
		DataInputStream dataIn = new DataInputStream(in);
		return dataIn.readLong();
	}

	public void setPosition(long pos) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutputStream dataOut = new DataOutputStream(baos);
		dataOut.write(COMMAND_SET_POSITION);
		dataOut.writeLong(pos);
		byte[] data = baos.toByteArray();
		dataOut.close();
		out.write(data);
	}

	public void exitPlayer() throws IOException {
		out.write(COMMAND_EXIT_PLAYER);
	}

	public MediaItem[] listDirectory(String dir) throws IOException {
		byte[] dirBytes = dir.getBytes();
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutputStream dataOut = new DataOutputStream(baos);
		dataOut.write(COMMAND_LIST_DIRECTORY);
		dataOut.writeInt(dirBytes.length);
		dataOut.write(dirBytes);
		byte[] buf = baos.toByteArray();
		dataOut.close();
		out.write(buf);
		MediaItem[] ret = null;
		synchronized (in) {
			DataInputStream dataIn = new DataInputStream(in);
			int numFiles = dataIn.readInt();
			ret = new MediaItem[numFiles];
			for (int ii = 0; ii < numFiles; ii++) {
				boolean directory = dataIn.readBoolean();
				int strLen = dataIn.readInt();
				buf = new byte[strLen];
				in.read(buf);
				ret[ii] = new MediaItem(new String(buf), directory);
			}
		}
		return ret;
	}

	public void setPlaylist(String[] mrls) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutputStream dataOut = new DataOutputStream(baos);
		dataOut.write(COMMAND_SET_PLAYLIST);
		dataOut.write((byte) mrls.length);
		for (int ii = 0; ii < mrls.length; ii++) {
			dataOut.writeInt(mrls[ii].length());
			dataOut.write(mrls[ii].getBytes());
		}
		byte[] data = baos.toByteArray();
		dataOut.close();
		out.write(data);
	}

	public int getRate() throws IOException {
		out.write(COMMAND_GET_RATE);
		DataInputStream dataIn = new DataInputStream(in);
		return dataIn.readInt();
	}

	public void setRate(int rate) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutputStream dataOut = new DataOutputStream(baos);
		dataOut.write(COMMAND_SET_RATE);
		dataOut.writeInt(rate);
		byte[] data = baos.toByteArray();
		dataOut.close();
		out.write(data);
	}

	public boolean isRunning() throws IOException {
		out.write(COMMAND_IS_RUNNING);
		return in.read() != 0;
	}

	public boolean isPaused() throws IOException {
		out.write(COMMAND_IS_PAUSED);
		return in.read() != 0;
	}

	public void skipForwardShort() throws IOException {
		out.write(COMMAND_SKIP_FORWARD_SHORT);
	}

	public void skipForwardLong() throws IOException {
		out.write(COMMAND_SKIP_FORWARD_LONG);
	}

	public void skipBackwardShort() throws IOException {
		out.write(COMMAND_SKIP_BACKWARD_SHORT);
	}

	public void skipBackwardLong() throws IOException {
		out.write(COMMAND_SKIP_BACKWARD_LONG);
	}

}
